package com.yang.frost.blueberry.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.yang.frost.blueberry.config.properties.BlueBerryProperties;
import com.yang.frost.blueberry.config.properties.JwtProperties;
import com.yang.frost.blueberry.config.properties.ShiroProperties;
import com.yang.frost.blueberry.constant.CommonRedisKey;
import com.yang.frost.blueberry.domain.SysDepartment;
import com.yang.frost.blueberry.domain.SysRole;
import com.yang.frost.blueberry.domain.SysUser;
import com.yang.frost.blueberry.enums.StateEnum;
import com.yang.frost.blueberry.mapper.SysRolePermissionMapper;
import com.yang.frost.blueberry.mapper.SysUserMapper;
import com.yang.frost.blueberry.params.BaseParam;
import com.yang.frost.blueberry.params.LoginParam;
import com.yang.frost.blueberry.service.LoginService;
import com.yang.frost.blueberry.service.SysDepartmentService;
import com.yang.frost.blueberry.service.SysRolePermissionService;
import com.yang.frost.blueberry.service.SysRoleService;
import com.yang.frost.blueberry.shiro.jwt.JwtToken;
import com.yang.frost.blueberry.shiro.service.LoginRedisService;
import com.yang.frost.blueberry.shiro.vo.LoginSysUserRedisVo;
import com.yang.frost.blueberry.shiro.vo.LoginSysUserTokenVo;
import com.yang.frost.blueberry.shiro.vo.LoginSysUserVo;
import com.yang.frost.blueberry.util.ApiResult;
import com.yang.frost.blueberry.util.JwtTokenUtil;
import com.yang.frost.blueberry.util.JwtUtil;
import com.yang.frost.blueberry.util.SaltUtil;
import com.yang.frost.blueberry.vo.SysDepartmentQueryVo;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author 10296
 * @date 2021/1/25
 * @Description: 登录系统服务实现类
 */
@Service
public class LoginServiceImpl implements LoginService {

    private Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);

    @Autowired
    private BlueBerryProperties blueBerryProperties;


    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysDepartmentService sysDepartmentService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private LoginRedisService loginRedisService;
    @Autowired
    private SysRolePermissionService sysRolePermissionService;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private ShiroProperties shiroProperties;


    @Override
    public ApiResult login(LoginParam loginParam) throws Exception {

        Map<String, Object> resultMap = new HashMap<>();
        //1. 校验验证码
        checkVerifyCode(loginParam.getVerifyToken(), loginParam.getCode());
        //
        String username = loginParam.getUsername();
        String password = loginParam.getPassword();

        Map<String, Object> params = new HashMap<>();
        params.put("username", username);
        params.put("password", password);
        // 从数据库中获取登录用户信息
        SysUser sysUser = getSysUserByPassWord(params);
        if (sysUser == null) {

//            throw new AuthenticationException("用户名或密码错误！");
            return ApiResult.fail("用户名或密码错误！");
        }
        if (StateEnum.DISABLE.getCode().equals(sysUser.getState())) {
//            throw new AuthenticationException("账号已禁用");
            return ApiResult.fail("账号已禁用");
        }

        // 将系统用户对象转换成登录用户对象
        LoginSysUserVo loginSysUserVo = new LoginSysUserVo();
        loginSysUserVo.setDepartmentId(sysUser.getDepartmentId());
        loginSysUserVo.setGender(sysUser.getGender());
        loginSysUserVo.setId(sysUser.getId());
        loginSysUserVo.setNickname(sysUser.getNickname());
        loginSysUserVo.setUsername(sysUser.getUsername());
        loginSysUserVo.setState(sysUser.getState());
        loginSysUserVo.setRoleId(sysUser.getRoleId());

        //获取部门信息
        SysDepartment sysDepartment = sysDepartmentService.getSysDepartmentById(sysUser.getDepartmentId());
        if (sysDepartment == null) {
//            throw new AuthenticationException("部门不存在");
            return ApiResult.fail("部门不存在");
        }
        if (!StateEnum.ENABLE.getCode().equals(sysDepartment.getState())) {
//            throw new AuthenticationException("部门已禁用");
            return ApiResult.fail("部门已禁用!");
        }
        loginSysUserVo.setDepartmentName(sysDepartment.getDeptName());
        // 获取当前用户角色
        Long roleId = sysUser.getRoleId();
        SysRole sysRole = sysRoleService.getById(roleId);
        if (sysRole == null) {
//            throw new AuthenticationException("角色不存在");
            return ApiResult.fail("角色不存在!");
        }
        if (StateEnum.DISABLE.getCode().equals(sysRole.getState())) {
//            throw new AuthenticationException("角色已禁用");
            return ApiResult.fail("角色已禁用!");
        }
        loginSysUserVo.setRoleCode(sysRole.getCode());
        loginSysUserVo.setRoleName(sysRole.getRoleName());
        // 获取当前用户权限
        Set<String> permissionCodes = sysRolePermissionService.getPermissionCodesByRoleId(roleId);
        if (CollectionUtils.isEmpty(permissionCodes)) {
//            throw new AuthenticationException("权限列表不能为空");
            return ApiResult.fail("权限列表不能为空!");
        }
        loginSysUserVo.setPermissionCodes(permissionCodes);

        // 获取数据库中保存的盐值
        String newSalt = SaltUtil.getSalt(sysUser.getSalt(), jwtProperties);

        // 生成token字符串并返回
        Long expireSecond = jwtProperties.getExpireSecond();

        String token = JwtUtil.generateToken(username, newSalt, Duration.ofSeconds(expireSecond));
        logger.debug("token:{}", token);

        // 创建AuthenticationToken
        JwtToken jwtToken = JwtToken.build(token, username, newSalt, expireSecond);

        boolean enableShiro = shiroProperties.isEnable();
        logger.info(enableShiro + "");
        if (enableShiro) {
            // 从SecurityUtils里边创建一个 subject
            Subject subject = SecurityUtils.getSubject();
            // 执行认证登录
            subject.login(jwtToken);
        } else {
            logger.warn("未启用Shiro");
        }

        // 缓存登录信息到Redis
        loginRedisService.cacheLoginInfo(jwtToken, loginSysUserVo);
        logger.info("登录成功,username: {}", username);

        // 缓存登录信息到redis
        String tokenSha256 = DigestUtils.sha256Hex(token);
        redisTemplate.opsForValue().set(tokenSha256, loginSysUserVo, 1, TimeUnit.DAYS);

        resultMap.put("token", token);
        resultMap.put("rows", JSONObject.toJSON(loginSysUserVo));

        return ApiResult.ok(resultMap);
    }

    @Override
    public void logout(BaseParam param) throws Exception {
        boolean enableShiro = shiroProperties.isEnable();
        if (enableShiro) {
            Subject subject = SecurityUtils.getSubject();
            //注销
            subject.logout();
        }
//        String token = JwtTokenUtil.getToken(request);

        //获取token
        String token = param.getToken();
        String userName = JwtUtil.getUserName(token);
        //删除redis缓存
        loginRedisService.deleteLoginInfo(token, userName);


    }

    @Override
    public SysUser getSysUserByUserName(String userName) throws Exception {

        return null;
    }

    @Override
    public SysUser getSysUserByPassWord(Map<String, Object> paramsMap) throws Exception {

        return sysUserMapper.getLoginSysUser(paramsMap);
    }

    @Override
    public void checkVerifyCode(String verifyToken, String code) throws Exception {

        boolean enableVerifyCode = blueBerryProperties.isEnableVerifyCode();
        //如果没有用验证码，则返回
        if (!enableVerifyCode) {
            return;
        }
        //校验验证码
        if (StringUtils.isBlank(code)) {
            throw new Exception("请输入验证码！");
        }
        //从redis中获取
        String redisKey = String.format(CommonRedisKey.VERIFY_CODE, verifyToken);
        String generateCode = (String) redisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isBlank(generateCode)) {
            throw new Exception("验证码已过期或不正确");
        }
        // 不区分大小写
        if (!generateCode.equalsIgnoreCase(code)) {
            throw new Exception("验证码错误");
        }
        // 验证码校验成功，删除Redis缓存
        redisTemplate.delete(redisKey);
    }

    @Override
    public void generateVerifyCode() throws Exception {



    }


    @Override
    public ApiResult verifyAuthToken(String token) {
        Map<String, Object> resultMap = new HashMap<>(2);
        boolean exists = loginRedisService.exists(token);
        if (exists) {
            LoginSysUserVo loginSysUserVo = loginRedisService.getLoginSysUserRedisVoByToken(token);
            resultMap.put("rows", JSONObject.toJSON(loginSysUserVo));
        } else {
            return ApiResult.fail("Token已过期");
        }
        resultMap.put("token", token);

        return ApiResult.ok(resultMap);
    }
}
